#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <pthread.h>
#define DEZMI 10000000

typedef struct{
    mpf_t a;
    mpf_t b;
    mpf_t p;
}Data;

Data D[14];

void *Value_A(void *k){
    printf("Inciando Value_A\n");
    int i;
    mpf_t v_sqrt;
    mpf_t temp;

    mpf_init_set_ui(v_sqrt, 0l);
    mpf_init_set_ui(temp, 0L);


    for(i=0; i<13; i++){
        gmp_printf("Executando A -- %Ff\n", D[i].a);
        mpf_sqrt(v_sqrt, D[i].a);
        //Update of An+1
        mpf_ui_div(temp, 1L, v_sqrt); // temp = 1 / sqrt(A(n))
        mpf_add(D[i+1].a, v_sqrt, temp); // A(n+1) = sqrt(A(n)) + (1/sqrt( A(n)))
        mpf_div_ui(D[i+1].a, D[i+1].a, 2L); // A(n+1) = (sqrt(A(n)) + (1/sqrt(A(n)))) / 2
    }

    //ptread_exit(void *k);
}

void *Value_B(void *k){
    printf("Inciando Value_B\n");
    int i;
    mpf_t v_sqrt;
    mpf_t temp;
    mpf_init_set_ui(v_sqrt, 0l);
    mpf_init_set_ui(temp, 0L);

    for(i=0; i<13; i++){

        while(!mpf_cmp_d(D[i].a, 0.0)){sleep(1);}
        gmp_printf("Executando B -- %Ff\n", D[i].b);
        mpf_sqrt(v_sqrt, D[i].a);
        //Update of Bn+1
        mpf_add_ui(temp, D[i].b, 1L); //temp = 1 + b(n)
        mpf_mul(D[i+1].b, temp, v_sqrt); //b(n+1) = (1 + b(n)) * sqrt(A(n))
        mpf_add(temp, D[i].a, D[i].b); //temp = a(n) + b(n)
        mpf_div(D[i+1].b, D[i+1].b, temp); // B(n+1) = (1 + b(n)) * sqrt(A(n)) / a(n) + b(n)
    }
}

void *Value_P(void *k){
    printf("Inciando Value_P\n");
    int i;
    mpf_t temp;

    mpf_init_set_ui(temp, 0L);


    for(i=0; i<13; i++){
        while(!mpf_cmp_d(D[i+1].a, 0.0) || !mpf_cmp_d(D[i+1].b, 0.0)){sleep(1);}
        gmp_printf("Executando P -- %Ff\n", D[i].p);
        //Update of Pn+1
        mpf_add_ui(temp, D[i+1].a, 1L); //temp = A(n+1) + 1
        mpf_mul(temp, temp, D[i].p); // temp = (A(n+1) + 1) * p(n)
        mpf_mul(D[i+1].p, temp, D[i+1].b); // P(n+1) = ( (A(n+1) + 1) * p(n) ) * b(n+1)
        mpf_add_ui(temp, D[i+1].b, 1L); // temp = (1 + B(n+1) )
        mpf_div(D[i+1].p, D[i+1].p, temp); // P(n+1) = ( (A(n+1) + 1) * p(n) ) * b(n+1) / (1 + B(n+1) )
    }

}

int main(){
    time_t start, end;
    pthread_t Thread_a, Thread_b, Thread_p;
    int i;
    int flag;
    mpf_t v_sqrt;
    mpf_t temp;

    start = time(NULL);

    mpf_set_default_prec(40000016);


    for(i=0; i<13; i++){
        mpf_init_set_ui(D[i].a, 0L);
        mpf_init_set_ui(D[i].b, 0L);
        mpf_init_set_ui(D[i].p, 0L);
    }


    mpf_sqrt_ui(D[0].a, 2L);


    mpf_add_ui(D[0].p, D[0].a, 2L);
    gmp_printf("Antes de entrar Ao = %Ff -- Po = %Ff\n", D[0].a, D[0].p);

    flag = pthread_create(&Thread_a, NULL, Value_A, (void *) D);
    if(flag) exit(-1);

    pthread_create(&Thread_b, NULL, Value_B, (void *) D);
    if(flag) exit(-1);

    pthread_create(&Thread_p, NULL, Value_P, (void *) D);
    if(flag) exit(-1);

    pthread_join(Thread_a, NULL);
    pthread_join(Thread_b, NULL);
    pthread_join(Thread_p, NULL);
    //mpf_out_str(NULL, 10, 40000016, D[i].p);
    gmp_printf("Value of PI: %.10Ff\n", D[12].p);
    end = time(NULL);
    printf("Execution time: %.2fs\n", difftime(end, start));

    return 0;
}
